﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.Practices.Unity;

namespace Commands.Commands
{
    class CommandsListAnalyzer
    {
        public static bool IsListWellDescribed(CommandListBase list, IUnityContainer container)
        {
            var methods = GetMethodsDictionary(list);

            // Check that all methods names are unique.
            if (!methods.Values.IsUnique(x => x))
                return false;

            // For each method check that its parameters are described by attributes and parameters names are unique.
            foreach (var method in methods)
            {
                if (!_IsMethodWellDescribed(method.Key, container))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Check that method parameters are fully described by attributes.
        /// </summary>
        private static bool _IsMethodWellDescribed(MethodInfo methodInfo, IUnityContainer container)
        {
            ParameterInfo[] methodParameters = methodInfo.GetParameters();
            foreach (var parameter in methodParameters)
            {
                // In case if type isn't registered in container or if container isn't exist at all.
                if (!container.IsRegistered(parameter.ParameterType))
                {
                    // And this parameter isn't described by attributes.
                    if (parameter.GetParameterAttribute() == null)
                        // Method isn't described well.
                        return false;
                }
            }

            // Check that all parameter names are unique.
            return methodInfo.GetParameters().Select(x => x.GetParameterAttribute()).
                Where(x => x != null).IsUnique(x => x.Names);
        }

        /// <summary>
        /// Get dictionary which maps method info to list of command names.
        /// </summary>
        public static Dictionary<MethodInfo, List<string>> GetMethodsDictionary(CommandListBase methodManager)
        {
            var result = new Dictionary<MethodInfo, List<string>>();
            var type = methodManager.GetType();
            var methods = type.GetMethods().Where(x => x.DeclaringType == type);
            foreach (var method in methods)
            {
                result.Add(method, new List<string>());
                var namesAttributes = method.GetCustomAttributes(typeof(RemoteCommand), false);
                foreach (RemoteCommand pseudoNameAttribute in namesAttributes)
                {
                    result[method].AddRange(pseudoNameAttribute.Names);
                }
            }
            return result;
        }
    }
}
